#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "openssl/err.h"
#include "openssl/evp.h"

typedef struct
{
        const unsigned char *in_data;
        size_t in_data_len;
        int in_data_is_already_padded;
        const unsigned char *in_ivec;
        const unsigned char *in_key;
        size_t in_key_len;
} test_case_t;

void handleErrors(void)
{
    ERR_print_errors_fp(stderr);
    abort();
}

int test_decrypt_with_cipher(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
                                                          unsigned char *iv, unsigned char *plaintext)
{
        EVP_CIPHER_CTX *ctx;
        int len;
        int plaintext_len;
        int i;
        int count;

        /* Create and initialise the context */
        if (!(ctx = EVP_CIPHER_CTX_new())) {
                handleErrors();
        }
        /*
     * Initialise the decryption operation. IMPORTANT - ensure you use a key
     * and IV size appropriate for your cipher
     * In this example we are using 256 bit AES (i.e. a 256 bit key). The
     * IV size for *most* modes is the same as the block size. For AES this
     * is 128 bits
     */

        // EVP_CIPHER_CTX_set_padding(ctx, 0);

        if (1 != EVP_DecryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key, iv)) {
                handleErrors();
        }
        /*
     * Provide the message to be decrypted, and obtain the plaintext output.
     * EVP_DecryptUpdate can be called multiple times if necessary.
     */
        EVP_CIPHER_CTX_set_padding(ctx, 0);
        // 解密数据只能按照向量的整数倍进行，这里向量的长度是16
        if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
                handleErrors();
        }

        printf("解密数据：%d\n",len);
        for (i = 0;i < 32;i++) {
                printf("%02x ",*(plaintext + i));
        }
        printf("\n");
        plaintext_len = len;

        EVP_CIPHER_CTX_free(ctx);

        return plaintext_len;
}

void test_encrypt_with_cipher(const test_case_t *in, const EVP_CIPHER *cipher, char *buf, int *len)
{
        unsigned char *out_buf = buf;
        int out_len;
        int out_padding_len;
        int i;
        EVP_CIPHER_CTX *ctx;

        ctx = EVP_CIPHER_CTX_new();
        EVP_EncryptInit_ex(ctx, cipher, NULL, in->in_key, in->in_ivec);

        //当填充之后会调用该函数
        if (in->in_data_is_already_padded)
        {
                /* Check whether the input data is already padded.
                And its length must be an integral multiple of the cipher's block size. */
                const size_t bs = EVP_CIPHER_block_size(cipher);
                printf("bs %d\n",bs);
                if (in->in_data_len % bs != 0)
                {
                        printf("ERROR-1: data length=%d which is not added yet; block size=%d\n", (int)in->in_data_len, (int)bs);
                        /* Warning: Remember to do some clean-ups */
                        EVP_CIPHER_CTX_free(ctx);
                        return;
                }
                /* Disable the implicit PKCS#7 padding defined in EVP_CIPHER */
                EVP_CIPHER_CTX_set_padding(ctx, 0);
        }

        out_len = 0;
        EVP_EncryptUpdate(ctx, out_buf, &out_len, in->in_data, in->in_data_len);
        out_padding_len = 0;
        EVP_EncryptFinal_ex(ctx, out_buf + out_len, &out_padding_len);

        EVP_CIPHER_CTX_free(ctx);
        *len = out_len + out_padding_len;
}

void main()
{
        unsigned char buf[5000];
        unsigned char buf_2[5000];
        int  len;
        int  i;
        const unsigned char data[] =
                {
                        0x32, 0x30, 0x31, 0x38, 0x31, 0x33, 0x32, 0x36,
                        0x0a
                };
        // 上面data 明文对应的密文
        const unsigned char data_c[] =
        {
                        0xe2, 0xcb, 0x21, 0x67, 0x93, 0xbc, 0xf6, 0x90,
                        0xe5, 0x44, 0xef, 0xc4, 0x89, 0x86, 0x83, 0xc5
                };
        unsigned char ivec[EVP_MAX_IV_LENGTH] =
                {
                        0x29, 0x8f, 0x9d, 0x82, 0xf4, 0x6f, 0xea, 0x45,
                        0xde, 0x3d, 0xaa, 0x6a, 0x41, 0x12, 0xf7, 0x0a
                };

        const unsigned char key1[16] = ///< key_data, 密钥内容, 至少16字节
                {
                        0x20, 0x18, 0x13, 0x26, 0x00, 0x12, 0x61, 0xbe,
                        0x12, 0x5d, 0x5a, 0x54, 0x5a, 0xba, 0xca, 0xb1
                };

        test_case_t tc;

        tc.in_data = data;
        tc.in_data_len = sizeof(data);
        tc.in_data_is_already_padded = (tc.in_data_len % 16) == 0; // Hard coded 16 as the cipher's block size
        tc.in_key = key1;
        tc.in_key_len = sizeof(key1);
        tc.in_ivec = ivec;

        printf("加密之前数据 %d\n",sizeof(data));
        for (i = 0;i < sizeof(data);i++) {
                printf("%02x ",data[i]);
        }
        printf("\n");

        memset(buf, 0 ,sizeof(buf));
        test_encrypt_with_cipher(&tc, EVP_sm4_cbc(),buf,&len);
        printf("加密之后数据 %d\n", len);
        for (i = 0;i < len;i++) {
                printf("%02x ",*(buf + i));
        }
        printf("\n");

        memset(buf_2, 0 ,sizeof(buf_2));
        len = test_decrypt_with_cipher(buf, 32, key1, ivec, buf_2);
        printf("解密之后数据 %d \n", len);
        for (i = 0;i < len;i++) {
                printf("%02x ",*(buf_2 + i));
        }
        printf("\n");
}
